﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Globalization;

namespace ChoWorkflowDashboard
{
    public partial class MonthViewControl
    {

        internal static System.DateTime _DisplayStartDate = System.DateTime.Now.AddDays(-1 * (System.DateTime.Now.Day - 1));
        private int _DisplayMonth = _DisplayStartDate.Month;
        private int _DisplayYear = _DisplayStartDate.Year;
        private static CultureInfo _cultureInfo = new CultureInfo(CultureInfo.CurrentUICulture.LCID);
        private System.Globalization.Calendar sysCal = _cultureInfo.Calendar;

        private List<ChoAppointment> _monthAppointments;
        public event DisplayMonthChangedEventHandler DisplayMonthChanged;
        public delegate void DisplayMonthChangedEventHandler(MonthChangedEventArgs e);
        public event DayBoxDoubleClickedEventHandler DayBoxDoubleClicked;
        public delegate void DayBoxDoubleClickedEventHandler(NewAppointmentEventArgs e);
        public event AppointmentDblClickedEventHandler AppointmentDblClicked;
        public event AppointmentMouseMovedEventHandler AppointmentMouseMoved;
        public delegate void AppointmentDblClickedEventHandler(object AppointmentId);
        public delegate void AppointmentMouseMovedEventHandler(DayBoxAppointmentControl source, object AppointmentId);

        public System.DateTime DisplayStartDate
        {
            get { return _DisplayStartDate; }
            set
            {
                _DisplayStartDate = value;
                _DisplayMonth = _DisplayStartDate.Month;
                _DisplayYear = _DisplayStartDate.Year;
            }
        }

        public List<ChoAppointment> MonthAppointments
        {
            get { return _monthAppointments; }
            set
            {
                _monthAppointments = value;
                BuildCalendarUI();
            }
        }

        private void  // ERROR: Handles clauses are not supported in C#
    MonthView_Loaded(object sender, RoutedEventArgs e)
        {
            //-- Want to have the calendar show up, even if no appoints are assigned 
            //   Note - in my own app, appointments are loaded by a backgroundWorker thread to avoid a laggy UI
            if (_monthAppointments == null)
                BuildCalendarUI();
        }
        public MonthViewControl()
        {
            InitializeComponent();

            if (_monthAppointments == null)
                BuildCalendarUI();
        }

        private void BuildCalendarUI()
        {
            int iDaysInMonth = sysCal.GetDaysInMonth(_DisplayStartDate.Year, _DisplayStartDate.Month);
            int iOffsetDays = Convert.ToInt32(System.Enum.ToObject(typeof(System.DayOfWeek), _DisplayStartDate.DayOfWeek));
            int iWeekCount = 0;
            WeekOfDaysControls weekRowCtrl = new WeekOfDaysControls();

            MonthViewGrid.Children.Clear();
            AddRowsToMonthGrid(iDaysInMonth, iOffsetDays);
            MonthYearLabel.Content = new DateTime(_DisplayYear, _DisplayMonth, 1).ToString("MMM", CultureInfo.InvariantCulture) + " " + _DisplayYear;
            for (int i = 1; i <= iDaysInMonth; i++)
            {
                if ((i != 1) && System.Math.IEEERemainder((i + iOffsetDays - 1), 7) == 0)
                {
                    //-- add existing weekrowcontrol to the monthgrid
                    Grid.SetRow(weekRowCtrl, iWeekCount);
                    MonthViewGrid.Children.Add(weekRowCtrl);
                    //-- make a new weekrowcontrol
                    weekRowCtrl = new WeekOfDaysControls();
                    iWeekCount += 1;
                }

                //-- load each weekrow with a DayBoxControl whose label is set to day number
                DayBoxControl dayBox = new DayBoxControl();
                dayBox.DayNumberLabel.Content = i.ToString();
                dayBox.Tag = i;
                dayBox.MouseDoubleClick += DayBox_DoubleClick;

                //-- customize daybox for today:
                if ((new System.DateTime(_DisplayYear, _DisplayMonth, i)) == System.DateTime.Today)
                {
                    dayBox.DayLabelRowBorder.Background = (Brush)dayBox.TryFindResource("OrangeGradientBrush");
                    dayBox.DayAppointmentsStack.Background = Brushes.Wheat;
                }

                //-- for design mode, add appointments to random days for show...
                //if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
                //{
                //    if (Microsoft.VisualBasic.Rnd(1) < 0.25)
                //    {
                //        DayBoxAppointmentControl apt = new DayBoxAppointmentControl();
                //        apt.DisplayText.Text = "Apt on " + i + "th";
                //        dayBox.DayAppointmentsStack.Children.Add(apt);
                //    }

                //}
                //else 
                    if (_monthAppointments != null)
                {
                    //-- Compiler warning about unpredictable results if using i (the iterator) in lambda, the 
                    //   "hint" suggests declaring another var and set equal to iterator var
                    int iday = i;
                    List<ChoAppointment> aptInDay = _monthAppointments.FindAll(new System.Predicate<ChoAppointment>((ChoAppointment apt) => Convert.ToDateTime(apt.StartTime).Day == iday));
                    foreach (ChoAppointment a in aptInDay)
                    {
                        DayBoxAppointmentControl apt = new DayBoxAppointmentControl();
                        apt.DisplayText.Text = a.Subject;
                        apt.Tag = a.AppointmentID;
                        apt.MouseDoubleClick += Appointment_DoubleClick;
                        apt.MouseMove += Appointment_MouseMove;
                        dayBox.DayAppointmentsStack.Children.Add(apt);
                    }

                }

                Grid.SetColumn(dayBox, (i - (iWeekCount * 7)) + iOffsetDays);
                weekRowCtrl.WeekRowGrid.Children.Add(dayBox);
            }
            Grid.SetRow(weekRowCtrl, iWeekCount);
            MonthViewGrid.Children.Add(weekRowCtrl);
        }

        private void AddRowsToMonthGrid(int DaysInMonth, int OffSetDays)
        {
            MonthViewGrid.RowDefinitions.Clear();
            System.Windows.GridLength rowHeight = new System.Windows.GridLength(60, System.Windows.GridUnitType.Star);

            int EndOffSetDays = 7 - (Convert.ToInt32(System.Enum.ToObject(typeof(System.DayOfWeek), _DisplayStartDate.AddDays(DaysInMonth - 1).DayOfWeek)) + 1);

            for (int i = 1; i <= Convert.ToInt32((DaysInMonth + OffSetDays + EndOffSetDays) / 7); i++)
            {
                var rowDef = new RowDefinition();
                rowDef.Height = rowHeight;
                MonthViewGrid.RowDefinitions.Add(rowDef);
            }
        }

        private void UpdateMonth(int MonthsToAdd)
        {
            MonthChangedEventArgs ev = new MonthChangedEventArgs();
            ev.OldDisplayStartDate = _DisplayStartDate;
            this.DisplayStartDate = _DisplayStartDate.AddMonths(MonthsToAdd);
            ev.NewDisplayStartDate = _DisplayStartDate;
            if (DisplayMonthChanged != null)
            {
                DisplayMonthChanged(ev);
            }
        }

        #region " UI Event Handlers "

        private void MonthGoPrev_MouseLeftButtonUp(System.Object sender, MouseButtonEventArgs e)
        {
            UpdateMonth(-1);
        }

        private void MonthGoNext_MouseLeftButtonUp(System.Object sender, MouseButtonEventArgs e)
        {
            UpdateMonth(1);
        }

        private void Appointment_DoubleClick(object sender, MouseButtonEventArgs e)
        {
            if (Object.ReferenceEquals(e.Source.GetType(), typeof(DayBoxAppointmentControl)))
            {
                if (((DayBoxAppointmentControl)e.Source).Tag != null)
                {
                    //-- You could put your own call to your appointment-displaying code or whatever here..
                    if (AppointmentDblClicked != null)
                    {
                        AppointmentDblClicked(((DayBoxAppointmentControl)e.Source).Tag);
                    }
                }
                e.Handled = true;
            }
        }

        private void Appointment_MouseMove(object sender, MouseEventArgs e)
        {
            if (Object.ReferenceEquals(e.Source.GetType(), typeof(DayBoxAppointmentControl)))
            {
                if (((DayBoxAppointmentControl)e.Source).Tag != null)
                {
                    //-- You could put your own call to your appointment-displaying code or whatever here..
                    if (AppointmentMouseMoved != null)
                    {
                        AppointmentMouseMoved(e.Source as DayBoxAppointmentControl, ((DayBoxAppointmentControl)e.Source).Tag);
                    }
                }
                e.Handled = true;
            }
        }

        private void DayBox_DoubleClick(object sender, MouseButtonEventArgs e)
        {
            ////-- call to FindVisualAncestor to make sure they didn't click on existing appointment (in which case,
            ////   that appointment window is already opened by handler Appointment_DoubleClick)

            if (object.ReferenceEquals(e.Source.GetType(), typeof(DayBoxControl)) && Utilities.FindVisualAncestor(typeof(DayBoxAppointmentControl), e.OriginalSource as System.Windows.Media.Visual) == null)
            {
                NewAppointmentEventArgs ev = new NewAppointmentEventArgs();
                if (((DayBoxControl)e.Source).Tag != null)
                {
                    ev.StartDate = new System.DateTime(_DisplayYear, _DisplayMonth, Convert.ToInt32(((DayBoxControl)e.Source).Tag), 10, 0, 0);
                    ev.EndDate = Convert.ToDateTime(ev.StartDate).AddHours(2);
                }
                if (DayBoxDoubleClicked != null)
                {
                    DayBoxDoubleClicked(ev);
                }
                e.Handled = true;
            }
        }

        #endregion

    }

    public struct MonthChangedEventArgs
    {
        public System.DateTime OldDisplayStartDate;
        public System.DateTime NewDisplayStartDate;
    }

    public struct NewAppointmentEventArgs
    {
        public System.DateTime? StartDate;
        public System.DateTime? EndDate;
        public int? CandidateId;
        public int? RequirementId;
    }

    public static class Utilities
    {
        //-- Many thanks to Bea Stollnitz, on whose blog I found the original C# version of below in a drag-drop helper class... 
        public static FrameworkElement FindVisualAncestor(System.Type ancestorType, System.Windows.Media.Visual visual)
        {
            while ((visual != null && !ancestorType.IsInstanceOfType(visual)))
            {
                visual = (System.Windows.Media.Visual)System.Windows.Media.VisualTreeHelper.GetParent(visual);
            }
            return (FrameworkElement)visual;
        }

    }
}
